<?php

namespace Common\Library;

/* Redis实例类
 * @version 1.0.0
 * @author hoff
 * @description 单例模式redis操作类
 */

class Redis {

    /**
     * @var object 对象单例
     */
    static private $_instance = null;

    /**
     * @var string Redis服务器连接地址
     */
    private $_hostname = '';

    /**
     * @var string Redis服务器端口
     */
    private $_port = '6379';

    /**
     * @var string Redis服务器密码
     */
    private $_pass = '';

    /**
     * @var int Redis服务器 - 索引
     */
    private $_db_index = 15;

    /**
     * @var array Redis服务器 - 索引集
     */
    private $_db_indexs;

    /**
     * @var int 目标数据库 索引
     */
    private $_focus_index;

    /**
     * @var object Redis连接对象
     */
    public $conn = null;

    /**
     * @var array Redis 允许的滚动 读取 操作
     */
    public $_roll_cmd = array('get', 'hgetall', 'hget', 'keys', 'ttl','zRevRank','zScore','zRange','zSize','zRevRange','zRangeByScore');

    /**
     * @var array Redis 允许的轮询 执行 操作
     */
    public $_poll_exe = array('set', 'hmset', 'hset', 'expire', 'del','zadd','hdel','zRemRangeByScore','zDelete');

    /**
     * 保证对象不被clone 
     */
    private function __clone() {
        
    }

    /**
     * 设置 数据库 - 索引
     * @param type $db_index
     */
    public function set_db_index($db_index) {
        if (is_array($db_index)) {
            $this->_db_indexs = $db_index;
            $db_index = array_shift($db_index);
        }
        $db_index = intval($db_index);
        if (0 <= $db_index && $db_index <= 15) {
            $this->_db_index = $db_index;
            $this->_focus_index = $db_index;
        }
    }

    /**
     * 构造函数
     * @param array $config  Redis连接配置
     */
    private function __construct($hostname, $port, $pass, $db_index) {
        $this->_hostname = $hostname;
        $this->_port = $port;
        $this->_pass = $pass;
        $this->set_db_index($db_index);
        $this->connect();
    }

    /**
     * 获得单例对象
     * @param array $config Redis连接配置
     * @param mixed $db_index 分库 - 索引
     * @return object
     */
    public static function getInstance($config = array(), $db_index) {
        if (!(self::$_instance instanceof self)) {
            if (!isset($db_index)) {
                E('数据单元未确定!');
            } else {
                self::$_instance = new self($config['hostname'], $config['port'], $config['pass'], $db_index);
            }
        }
        self::$_instance->set_db_index($db_index);
        self::$_instance->selectDb();
        return self::$_instance;
    }

    /**
     * 数据库连接
     */
    public function connect() {
        $this->conn = new \Redis() or trigger_error('缓存数据无法创建，请联系管理员', E_USER_ERROR);
        $this->conn->connect($this->_hostname, $this->_port);
        if (!empty($this->_pass)) {
            $this->conn->auth($this->_pass);
        }
        $this->selectDb($this->_db_index);
    }

    /**
     * 选择数据库
     * @param string $database
     */
    public function selectDb($db_index = '') {
        if (!empty($db_index)) {
            $this->conn->select($db_index);
            $this->_db_index = $db_index;
        } else {
            $this->conn->select($this->_db_index);
        }
    }

    private function check_cmd($cli) {
        return in_array($cli, $this->_roll_cmd);
    }

    private function check_exe($cli) {
        return in_array($cli, $this->_poll_exe);
    }

    /**
     * 
     * @param type $key
     * @param type $cli
     * @param type $param
     */
    public function poll($key, $cli, $param) {
        $result = false;
        if (!empty($key) && $this->check_exe($cli)) {
            if (is_array($this->_db_indexs)) {
                $rs = $this->roll($key, 'keys');
                if (!empty($rs)) {
                    $this->selectDb($this->_focus_index);
                } else {
//                    var_dump('rand....');
                    $index = array_rand($this->_db_indexs);
                    $this->_focus_index = $this->_db_indexs[$index];
                    $this->selectDb($this->_db_indexs[$index]);
                }
            }
            if($cli == "zadd" || $cli == "zRemRangeByScore"){
                foreach($param as $k=>$v){
                    $result = $this->conn->$cli($key,$k,$v);
                }
                
            }else{
                //    var_dump('poll:' . $this->_focus_index);
                 $result = $this->conn->$cli($key, $param);
            }
        
        }
        return $result;
    }

    /**
     * 设置 key 的剩余生存时间
     * @param type $key
     * @param type $timeout
     * @return type
     */
    public function expire($key, $timeout) {
        if (!empty($key) && !empty($timeout)) {
//            var_dump('expir:' . $this->_focus_index);
            if ($this->_focus_index != $this->_db_index) {
                $this->selectDb($this->_focus_index);
                $r = $this->conn->expire($key, $timeout);
                if ($r < 1) {
                    $r = $this->poll($key, 'expire', $timeout);
                }
            } else {
                $r = $this->poll($key, 'expire', $timeout);
            }
            $this->selectDb($this->_db_index);
            return $r;
        }
    }

    /**
     * 滚动执行 【查询】 操作
     * @param type $key
     * @param type $cli
     * @param int $deep 是否返回多个结果集
     * @return type
     */
    public function roll($key, $cli,$field=null, $deep = 0) {
        $val = false;
        //$arr_val = -1;
        if (!empty($key) && $this->check_cmd($cli)) {
            if ($this->_db_index != $this->_focus_index) {
                $this->selectDb($this->_focus_index);
            }
            if($field){
                
                if(is_array($field)){
                    $t = true;
                    if($cli == "zRangeByScore"){
                        $t = array('withscores' => TRUE);
                    }
                    foreach($field as $k=>$v){
                       $val = $this->conn->$cli($key,$k,$v,$t);
                   }
                }else{
                    $val = $this->conn->$cli($key,$field);  
                }
               
            }else{
                $val = $this->conn->$cli($key);  
            }
            if (empty($val) && is_array($this->_db_indexs)) {
                foreach ($this->_db_indexs as $k => $v) {
                    $this->selectDb($v);
                    $val = $this->conn->$cli($key);
                    if ($val) {
                        if (0 == $deep) {
                            $this->_focus_index = $v;
                            $this->selectDb($this->_db_index);
                            return $val;
                            break;
                        } elseif (1 == $deep) {
                            $this->_focus_index = $v;
                            foreach ($val as $vv) {
                                $arr_val[] = $vv;
                            }
                        }
                    }
                }
                return $arr_val;
            }
        }
        return $val;
    }

    /**
     * （废弃），可以直接使用 roll方法
     * 滚动获取 String类型的值
     * @param type $key 
     * @return type
     */
    public function rollGet($key) {
        $val = false;
        if (!empty($key)) {
            $val = $this->conn->get($key);
            if (!empty($val)) {
                return $val;
            } elseif (!empty($this->_db_indexs)) {
                if (is_array($this->_db_indexs)) {
                    foreach ($this->_db_indexs as $k => $v) {
                        $this->selectDb($v);
                        $val = $this->conn->get($key);
                        if (!empty($val)) {
                            $this->selectDb($this->_db_index);
                            break;
                        }
                    }
                }
            }
        }
        return $val;
    }

    /**
     * 关闭连接
     * @see DB_Base::close()
     */
    public function close() {
        unset($this->conn);
    }

    /**
     * 输出错误信息
     * @param string $msg
     * @param string $error
     * @param int $errorno
     */
    public function errorMsg($msg = '') {
        //输出错误信息
        $str = __CLASS__ . " {$msg}<br>
		<b>SQL</b>: {$this->sql}<br>";
        echo $str;
    }

    /**
     * 回收资源
     */
    public function __destruct() {
        if ($this->conn) {
            $this->close();
        }
    }

}
